//============================================================================
// Name        : Connection.cpp
// Author      : O.M.A.V.
// Description : Implementation file van de connection class
//============================================================================

#include "connection.h"

// Function prototypes
void *ServerThread(void *arg);
void *SocketThread(void *clientSocket);
void *SendThread(void *arg);
void HandleTCPClient(TCPSocket *sock);

// Constants
const int RCVBUFSIZE = 1024;

// Fields
list <TCPSocket *>clients;
list <char *>sendBuffer;
list <char *>receiveBuffer;

// Structures
struct ServerThreadStartArguments {
	char *interface;
	int port;
};

/*
 * Create a new connection.
 */
Connection::Connection(char *interface, int port)
{
	// Start the server thread
	ServerThreadStartArguments *arg = new ServerThreadStartArguments();
	arg->interface = interface;
	arg->port = port;

	pthread_t serverThreadID;
	if (pthread_create(&serverThreadID, NULL, ServerThread, (void *)arg) != 0) {
		cout << "Kan de server thread niet maken." << endl;
	}

	// Start the send thread
	pthread_t sendThreadID;
	if (pthread_create(&sendThreadID, NULL, SendThread, (void *)NULL) != 0) {
		cout << "Kan de send thread niet maken." << endl;
	}
}

Connection::~Connection()
{
}

/*
 * Sends data to all clients.
 */
void Connection::Send(char *data)
{
	sendBuffer.push_back(data);
}

/*
 * Reads data from the receive buffer.
 */
char *Connection::Receive()
{
	// Wait until there's data in the receiveBuffer list
	while(receiveBuffer.size() == 0)
	{
		usleep(250);
	}

	// Read, pop and return
	char *data = receiveBuffer.front();
	receiveBuffer.pop_front();

	return data;
}

/*
 * Thread that waits for connections.
 */
void *ServerThread(void *arg)
{
	ServerThreadStartArguments *carg = (ServerThreadStartArguments*)arg;

	// Get the interface's address
	HostInfo *info = new HostInfo();
	in_addr_t addrinfo = info->GetLocalIPAddress(carg->interface);
	char *address = info->IPAddressToChar(addrinfo);

	// Print server info
	cout << "Listening to port " << carg->port;
	cout << " on interface " << carg->interface;
	cout << " with IP address " << address << endl;

	// Listen
	TCPSocket serverSocket(carg->interface, carg->port, 5);
	while(true) {
		// Create seperate memory for client argument
		TCPSocket *clientSocket = serverSocket.accept();

		pthread_t threadID;
		if (pthread_create(&threadID, NULL, SocketThread, (void *)clientSocket) != 0) {
			cout << "Kan de client thread niet maken." << endl;
			exit(1);
		}
	}

	return NULL;
}

/*
 * Socket receive entry point.
 */
void *SocketThread(void *clientSocket)
{
	// Client connected
	TCPSocket *socket = (TCPSocket *)clientSocket;
	clients.push_back(socket);

	HandleTCPClient(socket);

	// Connection closed
	clients.remove(socket);
	delete (TCPSocket *)clientSocket;

	// Exit the thread
	return NULL;
}

/*
 * TCP Client handling function.
 */
void HandleTCPClient(TCPSocket *sock)
{
	try {
		// Receive information and echo it back
		char echoBuffer[RCVBUFSIZE];
		int rcvSize;
		while ((rcvSize = sock->recv(echoBuffer, RCVBUFSIZE)) > 0) {
			// Load into the receiveBuffer
			string buf(echoBuffer);
			receiveBuffer.push_back((char *)buf.c_str());

			// Clear the buffer
			memset(echoBuffer, 0, strlen(echoBuffer));
		}
	}
	catch (exception &ex) {
		cout << "Fout bij het afhandelen van client data." << endl;
	}
}

/*
 * Thread that sends data to clients.
 */
void *SendThread(void *arg)
{
	while(true) {
		// Required to keep the app responsive
		usleep(250);

		// Check for clients and data
		if (clients.size() > 0) {
			if (sendBuffer.size() > 0) {
				// Iterate through all sockets
				list<TCPSocket *>::iterator it;
				for (it = clients.begin(); it != clients.end(); it++) {
					// Get the socket to work with
					TCPSocket *sck = *it;

					// Send the first item of the send buffer
					if (sck != NULL) {
						char *data = sendBuffer.front();
						sck->send(data);
					}
				}

				// Remove the first item from the list
				sendBuffer.pop_front();
			}
		}
	}

	return NULL;
}
